#define	_GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include "../inc/assistant.h"
#include "../inc/graph.h"
#include "../inc/hash.h"
#include "../inc/fmanager.h"
#include "../inc/defs.h"
#include "../inc/list.h"
#include "../inc/log.h"
#include "../inc/scanner.h"
#include "../inc/parse.h"
#include "../inc/priorityQueue.h"

struct assistantCDT
{
	graphADT 		graph;
	fmanagerADT		airportStorage;
	fmanagerADT		flightStorage;
	FILE * 			optimusRoute;
	hashADT 		hash[2];
	insT			airImportMode;
	insT			flightImportMode;
	fmtT			outputMode;
	int				changes;
	int				airqtty;
};

typedef struct
{
	char	currday[DAY_MAX];	/* Campo que indica el dia actual en el que se esta en ese airport */
	int		arrivalTime;		/* horario en fmt militar de llegada */
	int		acumTime;			/* tiempo acumulado de viaje para findRoute */
	int		arrivalDays[DAYS];	/* Dia en que se llega a dicho aeropuerto */
} arrivalT;

typedef struct airportCDT
{
	char			name[AIR_MAX + 1];
	double			lat, lng;
	int				id;
	arrivalT		arrival;
} airportCDT;

typedef struct flightCDT
{
	int				flightnumber;
	char			name[FLIGHT_MAX + 1];	/* Nombre de la aerolinea */
	char			airfrom[AIR_MAX + 1];	/* Aeropuerto fuente */
	char			airto[AIR_MAX + 1];		/* Aeropuerto destino */
	weekdaysT		days[WEEKDAYS_MAX];		/* Dias de la semana que parte el vuelo */
	horaT			departure[DEPARTURE_MAX];/* Horario de salida */
	airtimeT		airtime[FTIME_MAX];		/* Duracion del vuelo */
	double			price;					/* Precio del vuelo */
} flightCDT;

typedef struct tripCDT
{
	listADT			flights;	/* Lista de vuelos que comparten aeropuerto fuente y destino */
	flightADT	*	optimus;	/* Vuelo optimo segun criterio (Precio, Duracion, Duracion + Tiempo Embarque) */
	int				ttime;		/* Tiempo total hasta tomar proximo vuelo, seteado cuando se calcula el peso de la arista por TT */
} tripCDT;

typedef struct
{
	int				prevflag;		/* flag que indica si la estructura ya contiene valores cargados. 1 = si, 0 = no */
	int				flighttime;
	int 			totaltime;		/* Tiempo total en el viaje de FindRoute entre SRC Y DST */
	double			totalprice;		/* Precio total del viaje entre SRC y DST */
	int				first;			/* Usado como flag para el aeropuerto inicial */
	airportADT		src;			/* Almacena de que aeropuerto inicia el recorrido */
	char			sweetDays[WEEKDAYS_MAX]; /* Mantiene los dias que el usuario prefiere iniciar el viaje */
} tripInfoT;

static assistantADT _assist = NULL;
static int first;
static tripInfoT	_tripinfo = {0}; /* Inicializa en 0 todos los campos */
typedef enum  {AIRPORT = 0, FLIGHT} dataT;

/* Definicion del prototipo de funcion que escribe informacion en kml o plain */
typedef void (* fnDump) (flightADT);

static int InitHashTables(assistantADT assist);
static int InitGraph(assistantADT assist);
static tripADT CreateTrip(flightADT flight, const char * airfrom, const char * airto);
static void FreeTrip(tripADT trip);
static flightCDT CreateFlightInfo(const char * name, int flightnumber, weekdaysT * days, char * airfrom, char * airto, horaT * departure, airtimeT * airtime, double price);
static int readAirports(FILE * airfile);
static int readFlights(FILE * flightfile);
static void freeArgs(char ** args, int cant);
static int CreateOptimus(flightADT * flights);
static void SetBestFlightPrice(flightADT flight);
static void SetBestFlightTime(flightADT flight);
static int convertTime(const char * airtime);
static int DigitQtty(int num);
static void EvalTripOptimus(tripADT trip, flightADT flight);
static int GetWaitingTime(airportADT src, weekdaysT * availabledays, int dephours, int acthours, int depminutes, int actminutes);
static int MiliHour( int hour, int min );
static int TotalWait( int * flightsday, int actday, int dephours, int acthours, int depminutes, int actminutes );
static int GetPositionDay( char *day );
static int GetBestFlightPrice(edgeElementT elem);
static int SelectConvenientPriceDay(airportADT src, tripADT trip);
static int GetBestFlightTime(edgeElementT elem);
static int SelectConvenientFTimeDay(airportADT src, tripADT trip);
static int GetBestFlightTotalTime(edgeElementT elem);
static int SetTotalTimeDays(tripADT trip, int * arrivalDays);
static void AdvanceADay(int * arrivalDays);
static void untagAllAirports(graphADT graph);
static int TagAirport(char * airportName, arrivalT tag);
static airportADT GetAirportFromGraph(airportADT airport);
static int SelectTTFlightDay(airportADT airport, tripADT trip, flightADT * best);
static int BestScale(int * arrivalDays, int arrivalTime, weekdaysT * days, horaT * departure, char * usefulDays);
static int DaysRemaining(int * week);
static int MiliMinutes(int militime);
static int MinutesMili(int minutime);
static int PrintRoute(prioT priority);
static void dumpPlainHeader(void);
static void dumpKMLHeader(void);
static void dumpKMLFoot(void);

static void fprintPlain(flightADT flight);
static void fprintKML(flightADT flight);
static void calculateArrivalTime(flightADT flight, int acthours, int actminutes, int militime);
static void SetCurrentDay(airportADT src, airportADT tgt, int acumdays);

/* Defincion de prototipos de las funciones call back de varios de los ADT usados */
static int Hash(void * elem, int size);
static int airCmp(void * elem1, void * elem2);
static int airCpy( void ** dst, void * src );
static void airFree( void * elem );
static int flightCmp(void * elem1, void * elem2);
static int flightCpy(void * elem1, void * elem2);
static void flightFree(void * elem);
static int tripCpy( void ** dst, void * src );
static void tripFree( void * elem );
static void freeOptimus(flightADT * flights);

assistantADT
NewAssistant(void)
{
	if (_assist != NULL)
		return _assist;

	if ( (_assist = calloc(1, sizeof(struct assistantCDT))) == NULL )
		return NULL;

	if (!InitHashTables(_assist))
		return NULL;

	if (!InitGraph(_assist))
		return NULL;

	if ( !LoadAirportsDB("airports.dat") )
	{
		Fatal("NewAssistant@assistantADT.c: - Could not load airports database, file missing\n");
		FreeAssistant();
		return NULL;
	}

	if ( !LoadFlightsDB("flights.dat") )
	{
		Fatal("NewAssistant@assistantADT.c: - Could not load flights database, file missing\n");
		FreeAssistant();
		return NULL;
	}

	_assist -> optimusRoute = stdout;
	_assist -> outputMode = PLAIN;

	return _assist;
}

int
InsertAirport(const char * name, double lat, double lng)
{
	airportCDT airport;

	if ( Precondicion(_assist) || Precondicion(name) )
	{
		Error("InsertAirport@assistantADT.c: - Invalid arguments\n");
		return FN_ERROR;
	}

	strcpy(airport.name, name);
	airport.lat = lat;
	airport.lng = lng;
	airport.id  = _assist -> airqtty;
	memset(&airport.arrival, 0, sizeof(arrivalT));

	if (!HInsert(_assist -> hash[AIRPORT], (hashElementT)&airport) )
	{
		Notification("InsertAirport@assistantADT.c: - %s airport already exist\n", name);
		Debug("InsertAirport@assistantADT.c: - %s airport exists in hash table\n", name);
		return FN_ERROR;
	}

	Debug("InsertAirport@assistantADT.c: - %s airport Inserted to hash table\n", name);

	if ( !NewNode(_assist -> graph, (nodeElementT)&airport) )
	{
		Error("InsertAirport@assistantADT.c: - %s airport NOT inserted\n");
		Debug("InsertAirport@assistantADT.c: - failed to add %s airport to graph\n");
		HDelete(_assist -> hash[AIRPORT], (hashElementT)&airport);
		return FN_ERROR;
	}

	Debug("InsertAirport@assistantADT.c: - %s airport Inserted to graph\n", name);

	_assist -> airqtty++;
	return 1;
}


/* A tener en cuenta: NewArc no debe crear los nodos si no existen, pues no se pueden crear
 * vuelos entre aeropuertos que aun no existen :), la funcion de comparacion de aeropuertos, en
 * caso de recibir como lat y lng un IGNR_LAT_LNG, no debe verificar por igualdad de lat y lng
 */
int
InsertFlight(const char * name, int flightnumber, weekdaysT * days, char * airfrom, char * airto, horaT * departure, airtimeT * airtime, double price)
{
	flightCDT flight;
	tripADT trip;

	if (Precondicion(_assist) )
	{
		Error("InsertFlight@assistantADT.c: - assistant not initialized\n");
		Debug("InsertFlight@assistantADT.c: - _assist is NULL\n");
		return FN_ERROR;
	}

	/* Arma la estructura de un vuelo */

	flight = CreateFlightInfo(name, flightnumber, days, airfrom, airto, departure, airtime, price);

	/* Inserta el vuelo en la tabla de hash para verificar la validez del mismo */
	if ( !HInsert(_assist -> hash[FLIGHT], (void *)&flight ) )
	{
		Notification("InsertFlight@assistantADT.c: - airline %s already exists\n", name);
		Debug("InsertFlight@assistantADT.c: - airline %s already inserted into hash table\n", name);
		return FN_ERROR;
	}

	/* Obtiene la ruta de vuelos entre aeropuerto airfrom y airto, si no existe se crea */
	if ( (trip = (tripADT)CreateTrip(&flight, airfrom, airto) ) == NULL )
	{
		HDelete(_assist -> hash[FLIGHT], (hashElementT)&flight );
		Debug("InsertFlight@assistantADT.c: - CreateTrip failed\n");
		return FN_ERROR;
	}

	/* Como la funcion de copia que se le envia a NewGraph, no reserva memoria
	 * para la lista, si no que directamente la asigna, al traerla, el grafo retornara
	 * el puntero a la lista y por ende al modificarla, se modificara en el grafo
	 */
	if ( !Insert(trip -> flights, (void *)&flight) )
	{
		Debug("InsertFlight@assistantADT.c: - Could not add flight %s to the list of flights between airport %s and %s\n", name, airfrom, airto);
		HDelete(_assist -> hash[FLIGHT], (hashElementT)&flight );
		
		/* Si falla insertar en la lista (por repetido o error), libera el trip pero
		 * no la lista  ya que tiene la referencia compartida con el grafo
		 */
		FreeTrip(trip);
		return FN_ERROR;
	}

	Notification("InsertFlight@assistantADT.c: - Added flight %s, number %d between airports %s and %s\n", flight.name, flightnumber, airfrom, airto);
	FreeTrip(trip);
	return 1;
}

int
MassiveAirInsert(const char * airDB, insT mode)
{
	FILE * airfile;
	hashADT hbkp1, hbkp2;

	hbkp1 = _assist -> hash[FLIGHT];
	hbkp2 = _assist -> hash[AIRPORT];

	if ( Precondicion(_assist) )
	{
		Error("MassiveAirInsert@assistantADT.c: - assistant not initialized\n");
		return FN_ERROR;
	}

	if ( Precondicion(airDB) )
	{
		Error("MassiveAirInsert@assistantADT.c: - Invalid arguments\n");
		return FN_ERROR;
	}

	if ( (airfile = fopen(airDB, "r+t")) == NULL )
		{
			Error("MassiveAirInsert@assistantADT.c: - failed to open file %s\n", airDB);
			return FN_ERROR;
		}

	if (mode == REPLACE)
	{
		/* Se borra la tabla de hash de los vuelos y aeropuertos, como tambien todas las aristas del grafo */
		_assist -> hash[FLIGHT] = NULL;
		_assist -> hash[AIRPORT] = NULL;
		FreeHash(hbkp1);
		FreeHash(hbkp2);
		removeAllEdges(_assist->graph);

		/* Inicializa nueva tabla de hash de vuelos */
		if ( (_assist -> hash[FLIGHT] = NewHash(sizeof(flightCDT), flightCmp, Hash)) == NULL )
		{
			Fatal("MassiveAirInsert@assistantADT.c: - Not enough memory\n");
			Debug("InitHashTables@assistantADT.c: - Not enough memory to create flight hash table\n");
			fclose(airfile);
			return FN_ERROR;
		}
		/* Inicializa nueva tabla de hash de aeropuertos */
		if ( (_assist -> hash[AIRPORT] = NewHash(sizeof(airportCDT), airCmp, Hash)) == NULL )
		{
			Fatal("MassiveAirInsert@assistantADT.c: - Not enough memory\n");
			Debug("InitHashTables@assistantADT.c: - Not enough memory to create airport hash table\n");
			fclose(airfile);
			return FN_ERROR;
		}

		_assist -> airImportMode = REPLACE;
	}
	else
		_assist -> airImportMode = APPEND;


	/* Si el archivo contiene errores o hay errores de lectura, no se reemplaza la base de datos */

	if ( readAirports(airfile) == FN_ERROR)
	{
		fclose(airfile);
		return FN_ERROR;
	}

	/* Si por consola de comandos se envio el parametro --replace, se pisa la base de datos
	 * con la provista por el usuario */
	if ( _assist -> airImportMode == REPLACE )
	{
		ClosefManager(_assist->airportStorage);
		if ( (_assist->airportStorage = NewfManager("airports.dat", sizeof(airportCDT))) == NULL )
		{
			Fatal("MassiveAirInsert@assistantADT.c: - airports data base not delete\n");
			return FN_ERROR;
		}
		Debug("MassiveAirInsert@assistantADT.c: - airports data base replaced\n");
	}

	fclose(airfile);
	return 1;
}

int
DeleteAirport(const char * name)
{
	airportCDT airport;
	flightADT flight;
	
	strcpy(airport.name, name);
	airport.lat = IGNR_LAT_LNG;
	airport.lng = IGNR_LAT_LNG;

	if ( !HDelete(_assist -> hash[AIRPORT], &airport) )
	{
		Debug("DeleteAirport@assistantADT.c - could not delete airport %s from hash table\n", name);
		return FN_ERROR;
	}

	if ( !removeNode(_assist -> graph, &airport) )
	{
		Debug("DeleteAirport@assistantADT.c - could not delete airport %s from graph\n", name);
		return FN_ERROR;
	}

	SetHashBegin(_assist -> hash[FLIGHT]);

	while( GetNextElement(_assist -> hash[FLIGHT], (void **)&flight) == 1 )
	{
		if ( strcmp(flight -> airfrom, name) == 0 || strcmp(flight -> airto, name) == 0 )
			if (HDelete(_assist -> hash[FLIGHT], flight) )
				Debug("DeleteAirport@assistantADT.c - flight %s number %d, deleted\n", flight -> name, flight -> flightnumber);
		
		flightFree(flight);
	}

	return 1;
}

int
DeleteFlight(const char * name, int flightNum)
{
	flightCDT flight;
	airportCDT tail, head;
	flightADT aux;
	tripADT trip;
	int gotedge;

	strcpy(flight.name, name);
	flight.flightnumber = flightNum;

	/* de la tabla de hash, se extrae el vuelo porque es necesaria la informacion del aeropuerto origen
	 * y destino, para poder traer la lista de vuelos entre esos aeropuertos y modificarla
	 */
	if ( (aux = GetHElement(_assist -> hash[FLIGHT], Lookup(_assist -> hash[FLIGHT], &flight)) ) == NULL )
	{
		Debug("DeleteFlight@assistantADT.c - could not get flight %s from hash table\n", name);
		return FN_ERROR;
	}

	strcpy(tail.name, aux -> airfrom);
	tail.lat = tail.lng = IGNR_LAT_LNG;

	strcpy(head.name, aux -> airto);
	head.lat = head.lng = IGNR_LAT_LNG;

	gotedge = GetEdge(_assist -> graph, (void *)&tail, (void *)&head, (void **)&trip);

	/* Si la arista no existe, el vuelo no existe */
	if (!gotedge)
	{
		Error("DeleteFlight@assistantADT.c - flight %s does not exist\n", name);
		Debug("DeleteFlight@assistantADT.c - flight %s does not exist in the graph\n", name);
		free(aux);
		return FN_ERROR;
	}

	/* Si la arista existe, pero en la lista de vuelos, el vuelo a borrar no existe, error */
	if (!Delete(trip -> flights, &flight))
	{
		Error("DeleteFlight@assistantADT.c - flight %s does not exist\n", name);
		Debug("DeleteFlight@assistantADT.c - flight %s does not exist in the list of flights between %s and %s\n", name, tail.name, head.name);
		freeOptimus(trip -> optimus);
		trip -> flights = NULL;
		free(trip);
		free(aux);
		return FN_ERROR;
	}

	/* si la lista quedo vacia, no hay mas conexiones entre esos aeropuertos */
	if ( listIsEmpty(trip -> flights) )
	{
		Debug("DeleteFlight@assistantADT.c - List of flights between %s and %s empty, edge removed\n", tail.name, head.name);
		removeEdge(_assist -> graph, &tail, &head);
	}
	else
		Debug("DeleteFlight@assistantADT.c - flight %s removed, flights remaining in list from %s to %s\n", flight.name, tail.name, head.name);
	/* Se borra el vuelo de la tabla de hash */
	if ( !HDelete(_assist -> hash[FLIGHT], &flight) )
	{
		Debug("DeleteFlight@assistantADT.c - flight %s not deleted\n", name);
		freeOptimus(trip -> optimus);
		trip -> flights = NULL;
		free(trip);
		free(aux);
		return FN_ERROR;
	}

	freeOptimus(trip -> optimus);
	trip -> flights = NULL;
	free(trip);
	free(aux);
	return 1;
}

int
MassiveFlightInsert(const char * flightDB, insT mode)
{
	FILE * flightfile;
	hashADT hbkp1;
	int rok;

	hbkp1 = _assist -> hash[FLIGHT];

	if ( Precondicion(_assist) )
	{
		Error("MassiveFlightsInsert@assistantADT.c: - assistant not initialized\n");
		return FN_ERROR;
	}

	if ( Precondicion(flightDB) )
	{
		Error("MassiveFlightsInsert@assistantADT.c: - Invalid arguments\n");
		return FN_ERROR;
	}

	if ( (flightfile = fopen(flightDB, "r+t")) == NULL )
		{
			Error("MassiveFlightInsert@assistantADT.c: - failed to open file %s\n", flightDB);
			return FN_ERROR;
		}

	if (mode == REPLACE)
	{
		/* Se borra la tabla de hash de los vuelos y todas las aristas del grafo */
		_assist -> hash[FLIGHT] = NULL;
		FreeHash(hbkp1);
		removeAllEdges(_assist->graph);

		/* Inicializa nueva tabla de hash de vuelos */
		if ( (_assist -> hash[FLIGHT] = NewHash(sizeof(flightCDT), flightCmp, Hash)) == NULL )
		{
			Fatal("MassiveFlightInsert@assistantADT.c: - Not enough memory\n");
			Debug("InitHashTables@assistantADT.c: - Not enough memory to create flight hash table\n");
			fclose(flightfile);
			return FN_ERROR;
		}

		_assist -> flightImportMode = REPLACE;
	}
	else
		_assist -> flightImportMode = APPEND;

	/* Si el archivo contiene errores o hay errores de lectura, no se reemplaza la base de datos */
	if ( (rok = readFlights(flightfile)) == 0 || rok == FN_ERROR )
	{
			fclose(flightfile);
			return rok;
	}

	/* Si por consola de comandos se envio el parametro --replace, se pisa la base de datos
	 * con la provista por el usuario */
	if ( _assist -> flightImportMode == REPLACE )
	{
		ClosefManager(_assist->flightStorage);
		if ( (_assist->flightStorage = NewfManager("flights.dat", sizeof(flightCDT))) == NULL )
		{
			Fatal("MassiveFlightInsert@assistantADT.c: - flights data base not delete\n");
			return FN_ERROR;
		}
		Debug("MassiveFlightInsert@assistantADT.c: -flights data base replaced\n");
	}

	fclose(flightfile);
	return 1;
}

int
MassiveAirDelete(void)
{
	fmanagerADT newairmanager;
	hashADT hbkp1, hbkp2;
	graphADT gbkp;

	if ( Precondicion(_assist) || Precondicion( _assist -> airportStorage ) )
		return FN_ERROR;
		
	if ( HTableIsEmpty(_assist -> hash[AIRPORT]) )
	{
		Error("MassiveAirDelete@assistantADT.c - Empty airport database, no action taken\n");
		return FN_ERROR;
	}

	hbkp1 = _assist -> hash[AIRPORT];
	hbkp2 = _assist -> hash[FLIGHT];
	gbkp = _assist -> graph;

	_assist -> hash[AIRPORT] = _assist -> hash[FLIGHT] = NULL;
	_assist -> graph = NULL;

	/* Inicializa nuevas tablas de hash */
	if ( !InitHashTables(_assist) )
		return FN_ERROR;

	/* Inicializa nuevo grafo */
	if ( !InitGraph(_assist) )
		return FN_ERROR;

	/* Cierra la base de datos anterior */
	ClosefManager(_assist -> airportStorage);

	/* Pisa la base de datos vieja con la nueva */
	if ( (newairmanager = OpenfManager("airports.dat", "w+", sizeof(airportCDT))) == NULL )
		return FN_ERROR;

	_assist -> airportStorage = newairmanager;

	/* Destruye las tablas de hash viejas y el grafo pues sin aeropuertos no puede existir
	 * ningun tipo de aerolinea previa ni un grafo pues no hay nodos */

	FreeHash(hbkp1);
	FreeHash(hbkp2);
	FreeGraph(gbkp);

	return 1;
}

int
MassiveFlightDelete(void)
{
	fmanagerADT newairmanager;
	hashADT hbkp, aux;

	if ( Precondicion(_assist) || Precondicion( _assist -> flightStorage ) )
		return FN_ERROR;

	if ( HTableIsEmpty(_assist -> hash[FLIGHT]) )
	{
		Error("MassiveFlightDelete@assistantADT.c - empty flights database, no action taken\n");
		return FN_ERROR;
	}

	hbkp = _assist -> hash[FLIGHT];

	_assist -> hash[FLIGHT] =  NULL;

	/* Inicializa nueva tabla de hash  de vuelos*/
	if ( ( aux = NewHash(sizeof(flightCDT), flightCmp, Hash)) == NULL )
		return FN_ERROR;

	_assist -> hash[FLIGHT] = aux;

	/* Cierra la base de datos anterior */
	ClosefManager(_assist -> flightStorage);

	/* Pisa la base de datos vieja con la nueva vacia*/
	if ( (newairmanager = OpenfManager("flights.dat", "w+", sizeof(flightCDT))) == NULL )
		return FN_ERROR;

	_assist -> flightStorage = newairmanager;

	/* Destruye la tabla de hash vieja de vuelos */
	FreeHash(hbkp);

	/* Borrar todas las aristas del grafo, que representan los vuelos */
	removeAllEdges(_assist->graph);

	return 1;
}

int
FindRoute(const char * src, const char * tgt, prioT priority, weekdaysT * wdays)
{
	airportCDT srcairport, tgtairport;
	airportADT aux;
	int dijok;

	if (Precondicion(_assist -> optimusRoute) )
	{
		Error("FindRoute@assistantADT.c - no output file specified, command Output \"file\" should be called first\n");
		return FN_ERROR;
	}

	untagAllAirports(_assist -> graph);

	if (priority == FT)
		SetWeightFunction( _assist -> graph, GetBestFlightTime );
	else if (priority == PR)
		SetWeightFunction(_assist -> graph, GetBestFlightPrice);
	else
		SetWeightFunction(_assist -> graph, GetBestFlightTotalTime);

	strcpy(srcairport.name, src);
	strcpy(tgtairport.name, tgt);

	srcairport.lat = srcairport.lng = tgtairport.lat = tgtairport.lng = IGNR_LAT_LNG;

	if(_tripinfo.prevflag == 1)
	{	
		memset(&_tripinfo, 0, sizeof(tripInfoT)); /* hace un clear a la estructura */
		_tripinfo.prevflag = 0;
	}
	
	if ( (aux = GetAirportFromGraph(&srcairport)) == NULL )
	{
		Error("FindRoute@assistantADT.c - %s does not exist\n", srcairport.name);
		return PATHERROR;
	}
	
	/* Inicia el aeropuerto src con tiempo acumulado y de arribo en 0 para
	 * poder calcular para los modos pr y ft, el dia y hora en que se va llegando
	 * a cada aeropuerto
	 */
	aux -> arrival.acumTime = -1;
	aux -> arrival.arrivalTime = 0;
	_tripinfo.src = aux;
	
	/* Si hay preferencia x dias, se almacenan */
	if (wdays != NULL)
		strcpy(_tripinfo.sweetDays, wdays);

	if ( (dijok = SetBeginDijkstra(_assist -> graph, &srcairport, &tgtairport)) == PATHERROR )
	{
		_tripinfo.prevflag = 1;
		return PATHERROR;
	}
	else if (dijok == 0)
	{
		_tripinfo.prevflag = 1;
		return FN_ERROR;
	}
	else
		dijok = PrintRoute(priority);

	_tripinfo.prevflag = 1;
	return dijok;

}

int
RouteFormat(fmtT format)
{
	if (Precondicion (_assist) )
	{
		Error("RouteFormat@assistantADT.c - Assistant not initialized\n");
		return FN_ERROR;
	}
	
	_assist -> outputMode = format;
	return 1;
}

int
RouteOutput(fd filedescriptor)
{
	if ( Precondicion(filedescriptor) )
	{
		Error("RouteOutput@assistantADT.c - invalid arguments\n");
		return FN_ERROR;
	}

	/* si habia un archivo previo, lo cierra */
	if (_assist -> optimusRoute != NULL && _assist -> optimusRoute != stdout)
		fclose(_assist -> optimusRoute);

	_assist -> optimusRoute = filedescriptor;

	return 1;
}


int
LoadAirportsDB(const char * airFile)
{
	int ok = 1;
	int size;
	airportCDT airport;

	if ( Precondicion(_assist) || Precondicion(airFile) )
		return 0;

	size = sizeof(struct airportCDT);

	if ( Precondicion(_assist) || Precondicion(airFile) )
		return 0;

	if ( (Precondicion(_assist -> airportStorage)) != 1 )
		ClosefManager(_assist -> airportStorage);

	if ( (_assist -> airportStorage = OpenfManager(airFile, "r+", size) ) == NULL )
	{
		Debug("LoadAirportsDB@assistantADT.c: - file does not exist, creating a new one\n");
		if ( (_assist -> airportStorage = NewfManager(airFile, size)) == NULL )
			ok = 0;
	}

	while ( fmread(_assist->airportStorage, 1, &airport) > 0 )
		InsertAirport(airport.name, airport.lat, airport.lng);

	return ok;
}

int
LoadFlightsDB(const char * flightFile)
{
	int ok = 1;
	int size;
	flightCDT flight;

	if ( Precondicion(_assist) || Precondicion(flightFile) )
		return FN_ERROR;

	size = sizeof(struct flightCDT);

	if ( (Precondicion(_assist ->  flightStorage)) != 1 )
		ClosefManager(_assist -> flightStorage);

	if ( (_assist -> flightStorage = OpenfManager(flightFile, "r+", size) ) == NULL )
	{
		Debug("LoadFlightsDB@assistantADT.c: - file does not exist, creating a new one\n");
		if ( (_assist -> flightStorage = NewfManager(flightFile, size)) == NULL )
			ok = 0;
	}

	while ( fmread(_assist->flightStorage, 1, &flight) > 0 )
		InsertFlight(flight.name, flight.flightnumber, (char *)(flight.days), flight.airfrom, flight.airto, (char *)(flight.departure), (char *)(flight.airtime), flight.price);

	return ok;
}

void
FreeAssistant(void)
{
	if (Precondicion(_assist))
	{
		Notification("FreeAssistant@assistantADT.c: - invalid assistant argument\n");
		Debug("FreeAssistant@assistantADT.c: - FreeAssistant invoked with null assistADT var\n");
		return;
	}

	airportADT airport = NULL;
	flightADT flight = NULL;

	ClosefManager(_assist -> airportStorage);
	ClosefManager(_assist -> flightStorage);
	
	if ( (_assist -> flightStorage = NewfManager("flights.dat", sizeof(flightCDT))) == NULL )
	{
		Fatal("FreeAssistantADT.c = Could not save flights\n");
		return;
	}
	
	if ( (_assist -> airportStorage = NewfManager("airports.dat", sizeof(airportCDT))) == NULL )
	{
		Fatal("FreeAssistantADT.c = Could not save airports\n");
		return;
	}
	
	SetHashBegin(_assist -> hash[FLIGHT]);
	SetHashBegin(_assist -> hash[AIRPORT]);
	
	while( GetNextElement(_assist -> hash[AIRPORT], (void **)&airport) )
	{	
		if (airport != NULL)
		{	
			fmwrite(_assist -> airportStorage, 1, airport);
			airFree(airport);
			airport = NULL;
		}
	}
	
	while( GetNextElement(_assist -> hash[FLIGHT], (void **)&flight) )
	{
		if (flight != NULL)
		{	
			fmwrite(_assist -> flightStorage, 1, flight);
			flightFree(flight);
			flight = NULL;
		}
	}
	
	ClosefManager(_assist -> airportStorage);
	ClosefManager(_assist -> flightStorage);
	
	FreeGraph(_assist -> graph);
	FreeHash(_assist -> hash[AIRPORT]);
	FreeHash(_assist -> hash[FLIGHT]);
	
	if (_assist -> optimusRoute != NULL)
		fclose(_assist -> optimusRoute);
	
	free(_assist);
	_assist = NULL;
	return;
}

static int
InitHashTables(assistantADT assist)
{
	if (Precondicion(assist))
	{
		Debug("InitHashTables@assistantADT.c: - invalid arguments");
		return FN_ERROR;
	}

	if ( (assist -> hash[AIRPORT] = NewHash(sizeof(airportCDT), airCmp, Hash)) == NULL )
	{
		Fatal("NewAssistant@assistantADT.c: - Not enough memory\n");
		Debug("InitHashTables@assistantADT.c: - Not enough memory to create airport hash table\n");
		FreeAssistant();
		return FN_ERROR;
	}

	if ( (assist -> hash[FLIGHT] = NewHash(sizeof(flightCDT), flightCmp, Hash)) == NULL )
	{
		Fatal("NewAssistant@assistantADT.c: - Not enough memory\n");
		Debug("InitHashTables@assistantADT.c: - Not enough memory to create flight hash table\n");
		FreeAssistant();
		return FN_ERROR;
	}

	return 1;
}

static int
InitGraph(assistantADT assist)
{
	if ( Precondicion(assist) )
	{
		Debug("InitGraph@assistantADT.c: - Invalid arguments\n");
		return FN_ERROR;
	}

	if ( (assist -> graph = NewGraph(airCmp, airCpy, airFree, tripCpy, tripFree) ) == NULL )
	{
		Fatal("NewAssistant@assistantADT.c: - Not enough memory\n");
		Debug("InitGraph@assistantADT.c: - Not enough memory to create internal graph\n");
		FreeAssistant();
		return FN_ERROR;
	}

	return 1;
}

static tripADT
CreateTrip(flightADT flight, const char * airfrom, const char * airto)
{
	tripADT trip;
	airportCDT tail, head;
	int gotEdge;

	/* Preparo la estructura de los aeropuertos origen y destino para traer la arista
	 * entre ellos
	 */
	strcpy(tail.name, airfrom);
	strcpy(head.name, airto);

	/* Le indica a la funcion de comparacion de aeropuertos que no debe comparar por lat y lng */
	tail.lng = tail.lat = IGNR_LAT_LNG;
	head.lng = head.lat = IGNR_LAT_LNG;

	/* Si alguno de los aeropuertos no existe, no crea el vuelo */
	if ( Lookup(_assist -> hash[AIRPORT], &tail) == -1 )
	{
		Error("airport %s does not exist, flight %s from %s to %s not created\n", tail.name, flight -> name, tail.name, head.name);
		return NULL;
	}

	if ( Lookup(_assist -> hash[AIRPORT], &head) == -1 )
	{
		Error("airport %s does not exist, flight %s from %s to %s not created\n", head.name, flight -> name, tail.name, head.name);
		return NULL;
	}

	/* Obtiene la arista entre el aeropuerto tail.name y head.name */
	gotEdge = GetEdge(_assist -> graph, (void *)&tail, (void *)&head, (void **)&trip);

	/* Si no existia ningun vuelo entre nodo origen y destino, se crea esa ruta */
	if (!gotEdge)
	{
		if ( (trip = calloc(1, sizeof(struct tripCDT))) == NULL )
		{
			Fatal("InsertFlight@assistantADT.c: - Not enough memory\n");
			return NULL;
		}

		if ( (trip -> optimus = calloc(PRIORITIES, sizeof(flightADT))) == NULL )
		{
			Fatal("InsertFlight@assistantADT.c: - Not enough memory\n");
			free(trip);
			return NULL;
		}

		/* Si la reserva de memoria es exitosa, se crea la ruta entre aeropuerto fuente y destino */\

		if ( (trip -> flights = Newlist(flightCmp, flightCpy, flightFree, sizeof(flightCDT))) == NULL )
		{
			Fatal("InsertFlight@assistantADT.c: - Not enough memory\n");
			Debug("CreateTrip@assistantADT.c: - Unable to create new list of flights for route between %s and %s\n", airfrom, airto);
			tripFree(trip);
			return NULL;
		}

		Debug("CreateTrip@assistantADT.c: - New list for airports %s and %s created\n", airfrom, airto);

		/* Reserva memoria para los 3 vuelos optimos */
		if ( !CreateOptimus(trip -> optimus) )
		{
			Fatal("InsertFlight@assistantADT.c: - not enough memory\n");
			Debug("CreateTrip@assistantADT.c - could not allocate memory for optimus flights\n");
			tripFree(trip);
			return NULL;
		}

		/* Como recien se crea la arista, se setea como mejor precio y tiempo de vuelo al primer
		 * vuelo insertado */
		flightCpy(trip -> optimus[PR - 1], flight);
		flightCpy(trip -> optimus[FT - 1], flight);

		if ( !NewArc(_assist -> graph, (nodeElementT)&tail, (nodeElementT)&head, (edgeElementT)trip ) )
		{
			Debug("CreateTrip@assistantADT.c: - Route from airport %s to airport %s could not be created\n", airfrom, airto);
			tripFree(trip);
			return NULL;
		}

		Debug("CreateTrip@assistantADT.c: - New edge for airports %s and %s created\n", airfrom, airto);
	}

	/* Actualiza de ser necesario los vuelos optimos por categoria menos el de tiempo total */
	EvalTripOptimus(trip, flight);

	Debug("CreateTrip@assistantADT.c: - best flight by price is %s of price %g\n", trip -> optimus[PR-1] -> name, (trip -> optimus[PR-1]) -> price);

	return trip;
}

static void
FreeTrip(tripADT trip)
{
	freeOptimus(trip -> optimus);
	trip -> flights = NULL;
	free(trip);
}

static flightCDT
CreateFlightInfo(const char * name, int flightnumber, weekdaysT * days, char * airfrom, char * airto, horaT * departure, airtimeT * airtime, double price)
{
	flightCDT flight = {0, {0}, {0}, {0}, {0}, {0}, {0}, 0};

	strncpy(flight.name, name, strlen(name));
	flight.name[strlen(name)] = '\0';
	flight.flightnumber = flightnumber;
	strncpy(flight.airfrom, airfrom, strlen(airfrom));
	flight.airfrom[strlen(airfrom)] = '\0';
	strncpy(flight.airto, airto, strlen(airto));
	flight.airto[strlen(airto)] = '\0';
	strncpy(flight.departure, departure, strlen(departure));
	flight.departure[strlen(departure)] = '\0';
	strncpy(flight.days, days, strlen(days));
	flight.days[strlen(days)] = '\0';
	strncpy(flight.airtime, airtime, strlen(airtime));
	flight.airtime[strlen(airtime)] = '\0';
	flight.price = price;
	return flight;
}

static int
readAirports(FILE * airfile)
{
	float lat, lng;
	char ** airbuffer;
	size_t strsize;
	char * lineaux = NULL;
	parseFn	fnparse[AIRARGS] = {parseName, parseLatitude, parseLongitude};
	int exit = 0, notconflicted = 1;
	int i;

	if ( (airbuffer = calloc(AIRARGS, sizeof(char *))) == NULL )
	{
		Fatal("MassiveAirInsert@assistantADT.c: - Not enough memory\n");
		return FN_ERROR;
	}

	while(!exit)
	{
		/* Lee una linea entera en *lineaux desde el archivo de aeropuertos */
		if ( !getline(&lineaux, &strsize, airfile) && !feof(airfile))
		{
			Error("MassiveAirInsert@assistantADT.c - Error reading airports file\n");
			free(airbuffer);
			return FN_ERROR;
		}

		/* si el archivo no finalizo, parseo la linea leida desde el archivo */
		if (!feof(airfile) )
		{
			/* Parte la linea obtenida con getline separadas por # y '\n' */
			if( !delimLine(airbuffer, lineaux, fnparse, AIRARGS ) )
			{
				Debug("readAirports@assistantADT.c - Error during airport argument parsing\n");
				free(lineaux);
				freeArgs(airbuffer, AIR_MAX);
				return FN_ERROR;
			}
			/* si llego aca es porque fue exitoso el parseo de todos los argumentos para la insercion de
			 * aeropuerto */
			 
			 if (airbuffer[1] == NULL )
			 	break;
			 	
			 sscanf(airbuffer[1], "%g", &lat);
			
			/* Si encontro lat, pero no lng, es un error */
			if (airbuffer[2] == NULL )
				return FN_ERROR;
				
			 sscanf(airbuffer[2],  "%g", &lng);

			 if ( InsertAirport(airbuffer[0], (double)lat, (double)lng) == FN_ERROR)
			 	notconflicted = FN_ERROR;

			 	
			for (i = 0 ; i < AIRARGS ; i++)
			{
				free(airbuffer[i]);
				airbuffer[i] = NULL;
			} 
			free(lineaux);
			lineaux = NULL;
		}
		else
			exit = 1;
	}

	free(lineaux);
	free(airbuffer);

	return notconflicted;
}

static int
readFlights(FILE * flightfile)
{
	char ** flightbuffer;
	size_t strsize;
	char * lineaux = NULL;
	parseFn	fnparse[FLIGHTARGS] = {parseName, parseFnum, parseDays, parseName, parseName, parseDep, parseFtime, parsePrice};
	int fnum, exit = 0;
	float price;
	int notconflicted = 1;
	int i;

	if ( Precondicion(_assist) )
	{
		return FN_ERROR;
	}

	if ( (flightbuffer = calloc(FLIGHTARGS, sizeof(char *))) == NULL )
	{
		Fatal("MassiveAirInsert@assistantADT.c: - Not enough memory\n");
		return FN_ERROR;
	}

	while(!exit)
	{
		/* Lee una linea entera en *lineaux desde el archivo de aerolineas */
		if ( !getline(&lineaux, &strsize, flightfile) && !feof(flightfile) )
		{
			Error("MassiveFlightInsert@assistantADT.c - Error reading airlines file\n");
			free(flightbuffer);
			return FN_ERROR;
		}

		/* si el archivo no finalizo, parseo la linea leida desde el archivo */
		if (!feof(flightfile) )
		{
			/* Parte la linea obtenida con getline separadas por # y '\n' */
			if( !delimLine(flightbuffer, lineaux, fnparse, FLIGHTARGS) )
			{
				Debug("readFlights@assistantADT.c - Error during flights argument parsing\n");
				free(lineaux);
				freeArgs(flightbuffer, FLIGHT_MAX);
				return FN_ERROR;
			}

			if (flightbuffer[1] == NULL )
			 	break;

			if (flightbuffer[2] == NULL )
				return FN_ERROR;

			sscanf(flightbuffer[1], "%d", &fnum);
			sscanf(flightbuffer[FLIGHTARGS - 1], "%g", &price);

			if ( InsertFlight( (const char *)flightbuffer[0], fnum, flightbuffer[2], flightbuffer[3], flightbuffer[4], flightbuffer[5], flightbuffer[6], (double) price) == FN_ERROR)
				notconflicted = FN_ERROR;
				
			for (i = 0 ; i < FLIGHTARGS ; i++)
			{
				free(flightbuffer[i]);
				flightbuffer[i] = NULL;
			} 
			free(lineaux);
			lineaux = NULL;
		}
		else
			exit = 1;
	}

	free(lineaux);
	freeArgs(flightbuffer, FLIGHT_MAX);

	return notconflicted;
}

static void
freeArgs(char ** args, int cant)
{
	int i ;

	for (i = 0 ; i < cant ; i++)
		free(args[i]);

	free(args);
	return;
}

/* La siguiente funcion de hash fue obtenida de la bibliografia provista por la catedra "Programming Abstractions in C, Eric Roberts" */
static int
Hash(void * elem, int size)
{
	int i;
	char * s;
	unsigned long hashcode;

	s = elem;

	hashcode = 0;
	for (i = 0 ; s[i] != '\0' ; i++)
		hashcode = hashcode * MULTIPLIER + s[i];

	return (hashcode % size);
}

static int
airCmp(void * elem1, void * elem2)
{
	int cmpbyname = 0;
	int len;
	airportADT aux1, aux2;

	aux1 = elem1;
	aux2 = elem2;

	if (aux1 -> lat == IGNR_LAT_LNG || aux2 -> lat == IGNR_LAT_LNG)
		cmpbyname = 1;

	if (cmpbyname)
	{
		len = strcmp(aux1 -> name, aux2 -> name);

		if (len < 0)
			return -1;
		else if (len == 0)
			return 0;
		else
			return 1;
	}
	
	return strcmp(aux1 -> name, aux2 -> name);
}

static int
airCpy( void ** dst, void * src )
{
	airportADT * aux1, aux2;
	int i;
	
	aux1 = (airportADT *)dst;
	aux2 = src;

	if ( ( (*aux1) = calloc(1, sizeof(airportCDT)) ) == NULL )
		return 0;

	strcpy( (*aux1) -> name, aux2 -> name);
	
	for (i = 0 ; i < DAYS ; i++)
		(*aux1) -> arrival.arrivalDays[i] = aux2 -> arrival.arrivalDays[i];
		
	(*aux1) -> lat = aux2 -> lat;
	(*aux1) -> lng = aux2 -> lng;
	(*aux1) -> id = aux2 -> id;
	(*aux1) -> arrival.arrivalTime = aux2 -> arrival.arrivalTime;
	(*aux1) -> arrival.acumTime = aux2 -> arrival.acumTime;
	strncpy((*aux1) -> arrival.currday, aux2 -> arrival.currday, 3);

	return 1;
}

static void
airFree( void * elem )
{
	free(elem);
}

static int
flightCmp(void * elem1, void * elem2)
{
	flightADT aux1, aux2;
	int len;

	aux1 = elem1;
	aux2 = elem2;

	if ( (len = strcmp(aux1 -> name, aux2 -> name)) == 0 && aux1 -> flightnumber == aux2 -> flightnumber)
		return 0;

	if (len == 0)
	{
		if (aux1 -> flightnumber - aux2 -> flightnumber < 0)
			return -1;
		else
			return 1;
	}

	return len;
}

static int
flightCpy(void * elem1, void * elem2)
{
	flightADT aux1, aux2;

	aux1 = elem1;
	aux2 = elem2;

	strcpy(aux1 -> name, aux2 -> name);
	
	aux1 -> flightnumber = aux2 -> flightnumber;
	strcpy(aux1 -> airfrom, aux2 -> airfrom);
	
	strcpy(aux1 -> airto, aux2 -> airto);
	
	strcpy(aux1 -> departure, aux2 -> departure);
	
	strcpy(aux1 -> days, aux2 -> days);
	
	strcpy(aux1 -> airtime, aux2 -> airtime);
	
	aux1 -> price = aux2 -> price;
	
	return 1;
}


static void
flightFree(void * elem)
{
	free(elem);
	return;
}

static int
tripCpy( nodeElementT *dst, nodeElementT src )
{
	tripADT * aux1, aux2;
	int i;

	aux1 = (tripADT *)dst;
	aux2 = src;

	if ( ( (*aux1) = calloc(1, sizeof(tripCDT))) == NULL )
		return 0;

	if ( ( (*aux1) -> optimus = calloc(PRIORITIES, sizeof(flightADT))) == NULL )
	{
		free( (*aux1) );
		return 0;
	}

	/* Reserva memoria para cada vuelo optimo , 1 x cada tipo de prioridad */
	if ( !CreateOptimus( (*aux1) -> optimus ) )
	{
		free(*(aux1) );
		return 0;
	}

	/* Copia los 3 vuelos optimos */
	for (i = 0 ; i < PRIORITIES ; i++ )
		flightCpy( ((*aux1) -> optimus)[i], (aux2 -> optimus)[i]);

	/* Se copia solo el puntero de la lista para que cuando se traiga del grafo una arista,
	 * que tiene adentro una lista, al modificarla quede modificada en el grafo. Idem con el vuelo
	 * optimo
	 */
	(*aux1) -> flights = aux2 -> flights;
	(*aux1) -> ttime = aux2 -> ttime;
	return 1;
}

static int
CreateOptimus(flightADT * flights)
{
	int i, j;
	for (i = 0 ; i < PRIORITIES ; i++)
	{
			/* si falla, libera los alocados hasta el momento */
		if ( (flights[i] = calloc(1, sizeof(flightCDT))) == NULL )
		{
			for (j = 0 ; j < i ; j++)
				free( flights[j]);
			free(flights);
			return 0;
		}
	}

	return 1;
}

static void
freeOptimus(flightADT * flights)
{
	int i;

	for(i = 0 ; i < PRIORITIES ; i++)
		flightFree(flights[i]);

	free(flights);
	return;
}

static void
tripFree( void * elem )
{
	tripADT aux;

	aux = elem;

	FreeList(aux -> flights);

	if (aux -> optimus != NULL)
		freeOptimus(aux -> optimus);

	free(aux);
	return;
}

static void
SetBestFlightPrice(flightADT flight)
{
	tripADT trip;
	airportCDT src, dst;
	
	src.lat = src.lng = dst.lat = dst.lng = IGNR_LAT_LNG;
	strcpy(src.name, flight -> airfrom);
	strcpy(dst.name, flight -> airto);
	
	trip = GetOriginalEdge(_assist -> graph, &src, &dst);
	flightCpy(trip -> optimus[PR - 1], flight);
	return;
}

static void
SetBestFlightTime(flightADT flight)
{
	tripADT trip;
	airportCDT src, dst;
	
	src.lat = src.lng = dst.lat = dst.lng = IGNR_LAT_LNG;
	strcpy(src.name, flight -> airfrom);
	strcpy(dst.name, flight -> airto);
	
	trip = GetOriginalEdge(_assist -> graph, &src, &dst);
	flightCpy(trip -> optimus[FT - 1], flight);
	return;
}

static void
EvalTripOptimus(tripADT trip, flightADT flight)
{
	int mhour1, mhour2;

	mhour1 = convertTime(trip -> optimus[FT - 1] -> airtime);
	mhour2 = convertTime(flight -> airtime);

	/* Si el tiempo del mejor vuelo actual es mayor al del nuevo vuelo, se actualiza el vuelo
	 * optimo
	 */
	if (mhour1 > mhour2)
		SetBestFlightTime(flight); /* actualiza el mejor vuelo */

	if (trip -> optimus[PR - 1] -> price > flight -> price)
		SetBestFlightPrice(flight);

	return;
}

static int
GetWaitingTime(airportADT src, weekdaysT * availabledays, int dephours, int acthours, int depminutes, int actminutes)
{
	char * token;
	char aux[WEEKDAYS_MAX] = {0};
	int flightdays[DAYS];
	int pos;

	strcpy(aux, availabledays);
	token = strtok(aux, "-\0");

	/* Setea el arreglo de flightdays en DIA INVALIDO */
	memset(flightdays, NOT_A_DAY, sizeof(int) * 7);

	/* Posiciona en el arreglo de ints el numero de dia en la posicion que le corresponde al dia */
	do
	{
		pos = GetPositionDay(token);
		flightdays[pos] = pos;
	}
	while ( (token = strtok(NULL, "-\0")) != NULL );

	/* obtiene la posicion del dia actual */
	pos = GetPositionDay(src -> arrival.currday);
	
	return TotalWait(flightdays, pos, dephours, acthours, depminutes, actminutes );	
}

static int
MiliHour( int hour, int min )
{
	return hour * 100 + min;
}

static int
TotalWait( int * flightsday, int actday, int dephours, int acthours, int depminutes, int actminutes )
{
	int actTime;
	int depTime;
	int totalhours = 0, totalminutes = 0;
	int i;
	
	actTime = MiliHour(acthours, actminutes);
	depTime = MiliHour(dephours, depminutes);
	
	/* caso particular en donde chequea que no haya salido todavia ese mismo dia */
	if( flightsday[actday] != -1 )
		if( actTime < depTime )
			return MiliHour( (dephours - acthours),  (depminutes-actminutes) );
		
	/* Si no habia vuelo ese mismo dia o habia pero ya se fue, calcula el tiempo hasta las 00:00hs
	 * del proximo dia
	 */
	totalhours = 23 - acthours;
	totalminutes = (60 - actminutes) % 60; 
	acthours = 0;
	actminutes = 0;
	
	for ( i = 0 ; i < DAYS ; i++ ) 
	{
		actday = (actday + 1) % DAYS; /* Suma dias circularmente */
		
		/* busca el vuelo mas cercano */
		if( flightsday[actday] != -1 ) 
		{
			totalhours +=  dephours;
			totalminutes += depminutes;
			break;
		}
		totalhours += 24;
	}
	
	return MiliHour(totalhours, totalminutes);
}

static int
GetPositionDay( char *day )
{
	char * weekDays[] = { "Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa" };
	int i;
	int flag = 1;

	for( i = 0; i < DAYS && flag; i++)
		if( strcmp ( day , weekDays[i] ) == 0 )
			flag = 0;

	/* -1 porque al preguntar x la condicion de corte se incremento i en 1 antes de salir */
	return (i-1);
}

static int
convertTime(const char * airtime)
{
	int hours = 0, minutes = 0;
	int rta;

	if ( sscanf(airtime, "%dh%dm", &hours, &minutes) != 2)
		sscanf(airtime, "%dm", &minutes);

	rta = hours * 100 + minutes;
	return rta;
}

static int
DigitQtty(int num)
{
	int i = 0;
	
	do
	{
		i++;
		num /=10;
	}
	while(num > 0);
	return i;
}

static int
GetBestFlightPrice(edgeElementT elem)
{
	tripADT aux;
	airportCDT srcaux;
	airportADT src;
	int waitingTime, acthours, actminutes, dephours, depminutes;
	int airtime;
	int totaltime;

	aux = elem;
	srcaux.lat = srcaux.lng = IGNR_LAT_LNG;
	strcpy(srcaux.name, aux -> optimus[PR - 1] -> airfrom);
	
	src = GetAirportFromGraph(&srcaux);

	/* Cada vez que se tome una arista entre el nodo inicial y otro destino, se chequea el dia
	 * de salida, pues a medida que se recorren varias aristas del nodo inicial, puede encontrarse
	 * un mejor vuelo segun criterio con otro dia de salida
	 */
	if (_tripinfo.src == src)
	{
		/* Si no hay preferencia de dias, se escoje el por default el primero que figura en la lista
		 * de dias para un vuelo. Si no elige el mas conveniente */ 
		if (*_tripinfo.sweetDays == '\0')
			strncpy(src -> arrival.currday, aux -> optimus[PR - 1] -> days, 2);
		else
		{	
			if ( !SelectConvenientPriceDay(src, aux) )
			{
				Error("FindRoute@assistantADT.c - No flights leaving on specified days\n");
				return INT_MAX;
			}
		}
		_tripinfo.prevflag = 1;
	}
	
	acthours = src -> arrival.arrivalTime / 100;
	actminutes = src -> arrival.arrivalTime % 100;
	
	sscanf(aux -> optimus[PR - 1] -> departure, "%02d:%02d", &dephours, &depminutes);

	/* calcula el tiempo de espera hasta embarque */
	waitingTime = GetWaitingTime(src, aux -> optimus[PR - 1] -> days, dephours, acthours, depminutes, actminutes);
	waitingTime = MiliMinutes(waitingTime);

	airtime = convertTime(aux -> optimus[PR - 1] -> airtime);

	/* tiempo total = tiempo de espera hasta embarque + duracion vuelo */
	totaltime = waitingTime + MiliMinutes(airtime);
	aux -> ttime = totaltime;

	/* Calcula dia y horario de llegada al proximo aeropuerto */
	calculateArrivalTime(aux -> optimus[PR - 1], acthours, actminutes, totaltime);

	return (aux -> optimus[PR - 1] -> price);
}

static int
SelectConvenientPriceDay(airportADT src, tripADT trip)
{
	char fdays[DEPARTURE_MAX] = {0};
	char mydays[DEPARTURE_MAX] = {0};
	char * mytok, * ftok;
	int dayfound = 0;
	int flightdayarray[DAYS], pos;
	flightCDT iterflight;
	
	
	SetBegin(trip -> flights);
	
	/* Recorre toda la lista de vuelos en busca de vuelos que partan los dias seleccionados y al 
	 * mejor precio */
	while( GetData(trip -> flights, &iterflight) == 1)
	{
		memset(flightdayarray, NOT_A_DAY, sizeof(int) * DAYS);
		strncpy(fdays, iterflight.days, strlen(iterflight.days));
		strncpy(mydays, _tripinfo.sweetDays, strlen(_tripinfo.sweetDays));
			
	
		ftok = strtok(fdays, "-\0");
	
		/* Carga un arreglo con dias de vuelos disponibles */
		do
		{
			pos = GetPositionDay(ftok);
			flightdayarray[pos] = pos;
		}
		while ( (ftok = strtok(NULL, "-\0")) != NULL );
		
		mytok = strtok(mydays, "-\0");
	
		/* Parte al string de dias preferidos en dias individuales y los matchea contra
		 * el arreglo de vuelos disponibles, si alguno matchea, actualiza de ser necesario el precio */
		do
		{
			pos = GetPositionDay(mytok);
			if ( flightdayarray[pos] == pos )
			{
				dayfound = 1;
				if (iterflight.price < trip -> optimus[PR - 1] -> price || _tripinfo.first == 0)
				{
					_tripinfo.first = 1;
					flightCpy(trip -> optimus[PR - 1], &iterflight);
					strcpy(src -> arrival.currday, mytok);
				}
			}
		}
		while ( !dayfound && (mytok = strtok(NULL, "-\0")) != NULL );
	}
	/* Si encontro el dia retorna success */
	if (dayfound)
		return 1;
		
	return 0;
}
	
static int
GetBestFlightTime(edgeElementT elem)
{
	tripADT aux;
	airportCDT srcaux;
	airportADT src;
	int waitingTime, acthours, actminutes, dephours, depminutes;
	int airtime;
	int totaltime;

	aux = elem;
	srcaux.lat = srcaux.lng = IGNR_LAT_LNG;
	strcpy(srcaux.name, aux -> optimus[PR - 1] -> airfrom);
	
	src = GetAirportFromGraph(&srcaux);
	
	if (_tripinfo.src == src)
	{
		/* Si no hay preferencia de dias, se escoje el por default el primero que figura en la lista
		 * de dias para un vuelo. Si no elige el mas conveniente */ 
		if (*_tripinfo.sweetDays == '\0')
		{
			strncpy(src -> arrival.currday, aux -> optimus[FT - 1] -> days, 2);
			src -> arrival.currday[2] = '\0';
		}
		else
		{	
			if ( !SelectConvenientFTimeDay(src, aux) )
			{
				Error("FindRoute@assistantADT.c - No flights leaving on specified days\n");
				return INT_MAX;
			}
		}
		_tripinfo.prevflag = 1;
	}

	acthours = src -> arrival.arrivalTime / 100;
	actminutes = src -> arrival.arrivalTime % 100;
	
	sscanf(aux -> optimus[FT - 1] -> departure, "%02d:%02d", &dephours, &depminutes);

	/* calcula el tiempo de espera hasta embarque en minutos*/
	waitingTime = GetWaitingTime(src, aux -> optimus[FT - 1] -> days, dephours, acthours, depminutes, actminutes);
	waitingTime = MiliMinutes(waitingTime);
	
	airtime = convertTime(aux -> optimus[FT - 1] -> airtime);
	totaltime = waitingTime + MiliMinutes(airtime);
	aux -> ttime = totaltime;

	calculateArrivalTime(aux -> optimus[FT - 1], acthours, actminutes, totaltime);

	return (airtime);

}

static int
SelectConvenientFTimeDay(airportADT src, tripADT trip)
{
	char fdays[DEPARTURE_MAX] = {0};
	char mydays[DEPARTURE_MAX] = {0};
	char * mytok, * ftok;
	int dayfound = 0;
	int flightdayarray[DAYS], pos;
	flightCDT iterflight;
	
	
	
	SetBegin(trip -> flights);
	
	/* Recorre toda la lista de vuelos en busca de vuelos que partan los dias seleccionados y al 
	 * mejor precio */
	while( GetData(trip -> flights, &iterflight) == 1)
	{
		memset(flightdayarray, NOT_A_DAY, sizeof(int) * DAYS);
		strncpy(fdays, iterflight.days, strlen(iterflight.days));
		strncpy(mydays, _tripinfo.sweetDays, strlen(_tripinfo.sweetDays));	
	
		ftok = strtok(fdays, "-\0");
	
		/* Carga un arreglo con dias de vuelos disponibles */
		do
		{
			pos = GetPositionDay(ftok);
			flightdayarray[pos] = pos;
		}
		while ( (ftok = strtok(NULL, "-\0")) != NULL );
		
		mytok = strtok(mydays, "-\0");
	
		/* Parte al string de dias preferidos en dias individuales y los matchea contra
		 * el arreglo de vuelos disponibles, si alguno matchea, actualiza de ser necesario el precio */
		do
		{
			pos = GetPositionDay(mytok);
			if ( flightdayarray[pos] == pos )
			{
				dayfound = 1;
				if (convertTime(iterflight.airtime) < convertTime(trip -> optimus[FT - 1] -> airtime) || _tripinfo.first == 0)
				{
					_tripinfo.first = 1;
					flightCpy(trip -> optimus[FT - 1], &iterflight);
					strcpy(src -> arrival.currday, mytok);
				}
			}
		}
		while ( dayfound && (mytok = strtok(NULL, "-\0")) != NULL );
	}
	/* Si encontro el dia retorna success */
	if (dayfound)
		return 1;
		
	return 0;

}

static int
GetBestFlightTotalTime(edgeElementT elem)
{
	airportADT airport;
	flightCDT currFlight;
	flightADT bestFlight;
    airportCDT src;
    arrivalT initial= {{0}, 0}; 
    arrivalT newArrival;
    int tweight, militime, hs, ms, pos, i;
    int wdays[DAYS];
    tripADT trip = elem;
    char auxdays[WEEKDAYS_MAX] = {0};
    char * tok;
    
    if(Precondicion(elem))
    {
    	Error("FindRoute@assistantADT.c - Error computing route\n");
    	Debug("GetBestFlightTotalTime@assistantADT.c - invalid trip\n");
        return -1;
    }
   	
    if ( _tripinfo.prevflag == 0 )
    {
    	if (*_tripinfo.sweetDays == '\0')
    	{
    		for (i = 0 ; i < DAYS ; i++)
				initial.arrivalDays[i] = i;
    	}
    	else
    		if ( !SetTotalTimeDays(trip, initial.arrivalDays) )
    			return INT_MAX;
		
		//Inicio a iterar por los vuelos
        SetBegin( trip -> flights );
        
        // Levanto el primer vuelo
        if (GetData(trip -> flights, &currFlight) != 0)
        {   
            if (!TagAirport( currFlight.airfrom, initial))
			{
				Error("FindRoute@assistantADT.c - Error computing route");
				Debug("GetBestFlightTotalTime@assistantADT.c - could not tag airport\n");
				return -1;
			}       
        }
        else
		{
			Debug("GetBestFlightTotalTime@assistantADT.c - No more flights\n");
			return INT_MAX; 
		}        

        _tripinfo.prevflag = 1;
    }

   	// Comienzo a iterar por los vuelos
    SetBegin(trip -> flights);
	
	// Tomo la informacion del nodo de donde salen los vuelos que me da informacion de la escala
    if (GetData( trip -> flights, &currFlight))
    {
    	
    	strcpy(src.name, currFlight.airfrom);
    	src.lat = src.lng = IGNR_LAT_LNG;
    	
		if ( (airport = GetAirportFromGraph(&src)) == NULL )
		{
			Debug("GetBestFlightTotalTime@assistantADT.c - Could not retrieve airport %s from graph\n", src.name);
			return -1;
		}
		
		/* Dado el aeropuerto de origen y la lista de vuelos, devuelve el mejor tiempo total
		 * del vuelo mas optimo (el vuelo  es almacenado en el parametro bestFlight)
		 */
		tweight = SelectTTFlightDay(airport, trip, &bestFlight);
		
		if (tweight == -1)
		{
			Debug("GetBestFlightTotalTime@assistantADT.c - Error looking for best flight by total time\n");
			return -1;
		}
	
		/* Como parametro del aeropuerto, se asigna ese tiempo acumulado hasta el momento
		 * para actualizarlo de ser necesario, pues dijkstra podria encontrar luego un camino
		 * que pase por el aeropuerto actual con menor tiempo acumulado
		 */
	
		militime = convertTime(bestFlight -> airtime);
		tweight +=  MiliMinutes(militime);
		newArrival.acumTime = tweight;
		
		/* Obtiene el tiempo de llegada con el que se llega a la conexion entre este aeropuerto y el vuelo
		 * elegido
		 */
		sscanf(bestFlight -> departure, "%02d:%02d", &hs, &ms);
		
		militime = hs * 100 + ms;
		
		/* Pasa ese tiempo a minutos */
		newArrival.arrivalTime =  MiliMinutes(militime) + MiliMinutes( convertTime(bestFlight -> airtime) );
		
		/* Obtiene en un arreglo de ints, los dias posibles con los que se llega al aeropuerto destino */
		strcpy(auxdays, bestFlight -> days);
		memset(wdays, NOT_A_DAY, DAYS * sizeof(int));
		
		tok = strtok(auxdays, "-\0");
		
		do
		{
			pos = GetPositionDay(tok);
			wdays[pos] = pos;
		}
		while( (tok = strtok(NULL, "-\0")) != NULL );
		
		/* Como parametro del aeropuerto destino, tambien se actualiza los dias posibles de llegada */
		memcpy(&newArrival.arrivalDays, wdays, DAYS * sizeof(int));
		memset(auxdays, 0, WEEKDAYS_MAX);
		
		while(newArrival.arrivalTime >= DAY_MINUTES)
		{
			/* Avanza un dia los dias posibles de llegada al aeropuerto destino */
			AdvanceADay(newArrival.arrivalDays);
			newArrival.arrivalTime = newArrival.arrivalTime - DAY_MINUTES;
		}
		
		/* Convierte el tiempo de arribo al aeropuerto destino en tiempo militar */
		newArrival.arrivalTime = MinutesMili(newArrival.arrivalTime); 
		
		/* Taguea el aeropuerto con los nuevos parametros de llegada, y si ya estaba tagueado,
		 * entonces el peso a retornar es INT_MAX indicando que la distancia al proximo aeropuerto
		 * no debe ser tenida en cuenta como la optima
		 */
		if ( !TagAirport( currFlight.airto, newArrival) )
		{
				Debug("GetBestFlightTotalTime@assistantADT.c - could not tag airport %s\n", currFlight.airto);
				return -1; 
		}
				
		flightFree( bestFlight );
    }
    else
    {
		Error("FindRoute@assistantADT.c - No flights going out from %s to %s\n", currFlight.airfrom, currFlight.airto);
		return INT_MAX; 
    }
    
    trip -> ttime = tweight;
	return tweight;
}

static int
SetTotalTimeDays(tripADT trip, int * arrivalDays)
{
	char fdays[WEEKDAYS_MAX] = {0};
	char mydays[WEEKDAYS_MAX] = {0};
	char * mytok, * ftok;
	int dayfound = 0;
	int flightdayarray[DAYS], pos;
	flightCDT iterflight;
	
	memset(arrivalDays, NOT_A_DAY, sizeof(int) * DAYS);
	SetBegin(trip -> flights);
	
	/* Recorre toda la lista de vuelos en busca de vuelos que partan los dias seleccionados */
	while( GetData(trip -> flights, &iterflight) == 1)
	{
		strncpy(fdays, iterflight.days, strlen(iterflight.days));
		memset(flightdayarray, NOT_A_DAY, sizeof(int) * DAYS);
		strncpy(mydays, _tripinfo.sweetDays, strlen(_tripinfo.sweetDays));
			
	
		ftok = strtok(fdays, "-\0");
	
		/* Carga un arreglo con dias de vuelos disponibles */
		do
		{
			pos = GetPositionDay(ftok);
			flightdayarray[pos] = pos;
		}
		while ( (ftok = strtok(NULL, "-\0")) != NULL );
		
		mytok = strtok(mydays, "-\0");
	
		/* Parte al string de dias preferidos en dias individuales y los matchea contra
		 * el arreglo de vuelos disponibles, si alguno matchea, actualiza los dias de arribo
		 * al aeropuerto inicial
		 */
		do
		{
			pos = GetPositionDay(mytok);
			if ( flightdayarray[pos] == pos )
			{
				arrivalDays[pos] = pos;
				dayfound = 1;
			}
		}
		while ( (mytok = strtok(NULL, "-\0")) != NULL );
	}
	/* Si encontro el dia retorna success */
	return dayfound;
}

static void
AdvanceADay(int * arrivalDays)
{
	int i;
	int aux, bkp;
	
	
	for ( i = 0, aux = arrivalDays[0] ; i < DAYS ; i++ )
	{
		bkp = arrivalDays[(i+1) % DAYS];
		if (aux == -1)
			arrivalDays[ (i+1) % DAYS ] = aux;
		else if (aux == 6)
			arrivalDays[ (i+1) % DAYS ] = 0;
		else
			arrivalDays[ (i+1) % DAYS ] = aux + 1;
		aux = bkp;
	}
}

static int
TagAirport(char * airportName, arrivalT tag)
{
	airportADT airport;
	airportCDT src;
	int i;
	
	strcpy(src.name, airportName);
	src.lat = src.lng = IGNR_LAT_LNG;
	
    if (( airport = GetAirportFromGraph(&src) ) == NULL )
	{
		Error("FindRoute@assistantADT.c - Errors computing FindRoute\n");
		Debug("FindRoute@assistantADT.c - Can't get from airport hashtable, \"airTo\" airport\n");
		return 0; 
	}
	
	// Si nunca se tagueo...
	if ( _tripinfo.prevflag == 0 || airport -> arrival.acumTime == -1 || airport -> arrival.acumTime > tag.acumTime)
	{
		airport -> arrival.acumTime = tag.acumTime;
		airport -> arrival.arrivalTime = tag.arrivalTime;
		
		for (i = 0 ; i < DAYS ; i++ )
			airport -> arrival.arrivalDays[i] = tag.arrivalDays[i];
	}
	else
		return 1;
	
	return 1;
}

static void
untagAllAirports(graphADT graph)
{
	airportADT aux;
	
	SetNodeIterator(graph);
	
	while ( (aux = GetNextNode(graph)) != NULL )
	{
		aux -> arrival.acumTime = -1;
		aux -> arrival.arrivalTime = -1;
		memset(aux -> arrival.arrivalDays, NOT_A_DAY, DAYS * sizeof(int));
	}
	
	return;
}		

static airportADT
GetAirportFromGraph(airportADT airport)
{
	airportADT aux=NULL;
	SetNodeIterator(_assist -> graph);
	int stop = 0;
	
	
	while (!stop && (aux = GetNextNode(_assist -> graph)) != NULL )
	{
		if (strcmp( aux -> name, airport -> name) == 0)
			stop = 1;	
	}
	
	return aux;	
}
	
static int
SelectTTFlightDay(airportADT airport, tripADT trip, flightADT * best)
{
	int minTotalTime = INT_MAX, totalTime;
	flightCDT currFlight;
	int bestDelayTime;
	char usefulDays[WEEKDAYS_MAX] = {0};
	
	if ( ((*best) = calloc(1, sizeof(struct flightCDT))) == NULL)
		return -1;
		
	// Recorro los vuelos
    SetBegin(trip -> flights);
    
    while(GetData(trip -> flights, &currFlight) != 0)
    {
			
		memset(usefulDays, 0, WEEKDAYS_MAX);
		bestDelayTime = BestScale(airport -> arrival.arrivalDays, airport -> arrival.arrivalTime, currFlight.days, 
						currFlight.departure, usefulDays);
						
	    totalTime = bestDelayTime;

        if ( totalTime < minTotalTime )
        {
            minTotalTime = totalTime;

			/* Guarda como rta, la informacion necesaria del mejor vuelo */
            strcpy((*best) -> airtime, currFlight.airtime);
            strcpy((*best) -> departure, currFlight.departure);
            strcpy((*best)-> days, usefulDays);
    		flightCpy(trip -> optimus[TT - 1], &currFlight); /* Almacena el vuelo optimo */            
		}
    }
	return minTotalTime;
}	

static int
BestScale(int * arrivalDays, int arrivalTime, weekdaysT * availdays, horaT * departure, char * destDays)
{
	char * tok, wdays[WEEKDAYS_MAX] = {0};
	char * days[DAYS] = {"Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"};
	int minWeek = 24*7*60;
	int min = INT_MAX, aux;
	int hs, ms;
	int delayTime, onPlaneTime, totalDelay, totalFlight;
	int auxarrivaldays[DAYS], auxavaildays[DAYS], auxdestdays[DAYS], availDaysbkp[DAYS];
	int i, j, pos, first = 1;
	
	/* Inicia el arreglo de dias de vuelos disponibles, y el de posibles dias de llegada */
	memset(auxavaildays, NOT_A_DAY, sizeof(int) * DAYS);
	memset(auxdestdays, NOT_A_DAY, sizeof(int) * DAYS);
	memset(availDaysbkp, NOT_A_DAY, sizeof(int) * DAYS);
	
	/* Se va a necesitar una copia de los dias de llegada para ir destruyendolo a medida
	 * que se leen los dias
	 */
	memcpy(auxarrivaldays, arrivalDays, sizeof(int) * DAYS);
	strcpy(wdays, availdays);
	
	
	/* Obtiene en minutos, el horario de llegada, y el tiempo en el aire */
	
	delayTime = MiliMinutes(arrivalTime);
	
	sscanf(departure, "%02d:%02d", &hs, &ms);
	
	onPlaneTime = hs * 100 + ms;
	onPlaneTime = MiliMinutes(onPlaneTime);

	/* Setea los dias en que parten vuelos */
	tok = strtok(wdays, "-\0");
	
	do
	{
		pos = GetPositionDay(tok);
		auxavaildays[pos] = availDaysbkp[pos] = pos;
	}
	while( (tok = strtok(NULL, "-\0")) != NULL );

	/* Por cada dia posible que se llega al aeropuerto, se evalua el tiempo hasta el proximo vuelo
	 * en minutos. El que tenga menor tiempo, se almacena en el parametro usefulDays.
	 * No necesariamente hay un unico dia optimo de llegada al aeropuerto, luego pueden haber
	 * varios "usefulDays". Usefuldays indica los posibles dias en que se llega al aeropuerto
	 * destino. Esta funcion retornara el minimo tiempo de espera hasta el proximo vuelo en su nombre
	 * y como ya se dijo, los posibles dias de llegada al proximo aeropuerto
	 */
	for( i = 0 ; DaysRemaining(auxarrivaldays) ; i++ )
	{
		memcpy(auxavaildays, availDaysbkp, sizeof(int) * DAYS);
		
		if (auxarrivaldays[i] != NOT_A_DAY)
		{
			auxarrivaldays[i] = NOT_A_DAY;
			totalDelay = delayTime + 24 * 60 * i;
			
			for ( j = 0 ; DaysRemaining(auxavaildays) ; j++ )
			{
				totalFlight = onPlaneTime + 24 * 60 * j;
				
				if (auxavaildays[j] != NOT_A_DAY)
				{
					auxavaildays[j] = NOT_A_DAY;
					aux = totalFlight - totalDelay;
					
					if (aux < 0 )
						aux += minWeek;
						
					if (aux < min)
					{
						min = aux;
						memset(auxdestdays, NOT_A_DAY, DAYS * sizeof(int));
						auxdestdays[j] = j;
					}
					
					if (aux == min)
						auxdestdays[j] = j;
				}
			}					
		}
	}
	
	/* Una vez que tiene el vector con los dias posibles de llegada asegurados por menor tiempo de escala
	 * los copia al string en fmt Lu-Ju-Sa... etc
	 */
	for (i = 0 ; i < DAYS ; i++ )
	{
		if (auxdestdays[i] != NOT_A_DAY)
		{
			if (!first)
				strcat(destDays, "-");
				
			first = 0;
			strcat(destDays, days[i]);
		}
	}
				 
	return min;	
}

static int
DaysRemaining(int * week)
{
	int i, remaining = 0;
	
	for (i = 0 ; i < DAYS && !remaining ; i++ )
		if (week[i] != NOT_A_DAY)
			remaining = 1;
			
	return remaining;
}

static int
MiliMinutes(int militime)
{
	int hours, minutes;
	minutes = militime % 100;
	hours = militime / 100;
	return minutes + 60 * hours;
}

static int
MinutesMili(int minutime)
{
	int hours, minutes;
	minutes = minutime % 60;
	hours = (minutime / 60) % 24;
	
	return (hours * 100 + minutes);
	
}

static int
PrintRoute(prioT priority)
{
	tripADT trip;
	fnDump filewriters[2] = {fprintPlain, fprintKML};
	pqueueADT pqueue;
	
	if ( (pqueue = NewPQueue() ) == NULL )
		return FN_ERROR;
	
	while ( (trip = (tripADT)GetNextDijkstraEdge(_assist -> graph)) != NULL )
	{
		_tripinfo.totalprice += trip -> optimus[priority - 1] -> price;
		_tripinfo.flighttime += MiliMinutes(convertTime(trip -> optimus[priority - 1] -> airtime));
		_tripinfo.totaltime  += trip -> ttime;
		PEnqueue(pqueue, trip, 1);
	}

	/* Transforma el tiempo total actualmente representado en minutos, a hora militar */
	
	//_tripinfo.totaltime = MiliHour(_tripinfo.totaltime / 60, _tripinfo.totaltime % 60);
	//_tripinfo.flighttime = MiliHour(_tripinfo.flighttime / 60, _tripinfo.flighttime % 60);
	
	/* dumpea el header modo texto */
	if (_assist -> outputMode == PLAIN)
		dumpPlainHeader();
		
	else
		dumpKMLHeader();
		
	
	while( !PQueueIsEmpty(pqueue) )
	{
			trip = PDequeue(pqueue);
			filewriters[_assist -> outputMode](trip -> optimus[priority - 1]);
			freeOptimus(trip -> optimus);
			trip -> flights = NULL;
			free(trip);
	}
	
	if (_assist -> outputMode == KML)
		dumpKMLFoot();

	FreePQueue(pqueue);

	return 1;
}


static void
dumpPlainHeader(void)
{
	char * ftime, * ttime;
	
	if ( (ftime = calloc( DigitQtty(_tripinfo.flighttime / 60) + 5, sizeof(char))) == NULL )
	{
		Error("FindRoute@assistantADT.c - not enough memory\n");
		Debug("unconvertTime@assistantADT.c - failed to allocate memory for time string\n");
		return;
	}
	
	if ( (ttime = calloc( DigitQtty(_tripinfo.totaltime / 60) + 5, sizeof(char))) == NULL )
	{
		Error("FindRoute@assistantADT.c - not enough memory\n");
		Debug("unconvertTime@assistantADT.c - failed to allocate memory for time string\n");
		return;
	}
	
	sprintf(ftime, "%0dh%02dm", _tripinfo.flighttime / 60, _tripinfo.flighttime % 60);
	sprintf(ttime, "%0dh%02dm", _tripinfo.totaltime / 60, _tripinfo.totaltime % 60);
	//ftime = unconvertTime(_tripinfo.flighttime);
	//ttime = unconvertTime(_tripinfo.totaltime);

	fprintf(_assist -> optimusRoute, "Precio#%g\n", (double)_tripinfo.totalprice);
	fprintf(_assist -> optimusRoute, "TiempoVuelo#%s\n", ftime);
	fprintf(_assist -> optimusRoute, "TiempoTotal#%s\n", ttime);
	fprintf(_assist -> optimusRoute, "\n");
	free(ftime);
	free(ttime);
	return;
}

static void
dumpKMLHeader(void)
{

	fprintf(_assist -> optimusRoute, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
	fprintf(_assist -> optimusRoute, "<kml xmlns=\"http://earth.google.com/kml/2.1\">\n");
	fprintf(_assist->optimusRoute, "<Document>\n");
	fprintf(_assist->optimusRoute, "  <name>OptimusR.kml</name>\n");
	fprintf(_assist->optimusRoute, "  <open>1</open>\n");
	fprintf(_assist->optimusRoute, "  <Style id=\"optimus\">\n");
	fprintf(_assist->optimusRoute, "    <LineStyle>\n");
	fprintf(_assist->optimusRoute, "      <color>7f0000ff</color>\n");
	fprintf(_assist->optimusRoute, "      <width>4</width>\n");
	fprintf(_assist->optimusRoute, "    </LineStyle>\n");
	fprintf(_assist->optimusRoute, "  </Style>\n");

	return;
}

static void
dumpKMLFoot(void)
{
	fprintf(_assist->optimusRoute, "</Document>\n");
	fprintf(_assist->optimusRoute, "</kml>\n");
	return;
}

static void
fprintPlain(flightADT flight)
{
	fprintf(_assist -> optimusRoute, "%s#%s#%d#%s\n", flight -> airfrom, flight -> name, flight -> flightnumber, flight -> airto);
	return;
}

static void
fprintKML(flightADT flight)
{
	airportCDT src, dst;
	airportADT aux1, aux2;
	
	strcpy(src.name, flight -> airfrom);
	strcpy(dst.name, flight -> airto);
	
	src.lat = src.lng = dst.lat = dst.lng = IGNR_LAT_LNG;

	aux1 = GetHElement(_assist -> hash[AIRPORT], Lookup(_assist -> hash[AIRPORT], &src) ); 
	aux2 = GetHElement(_assist -> hash[AIRPORT], Lookup(_assist -> hash[AIRPORT], &dst) );
		
	if(first) {
		fprintf(_assist->optimusRoute, "  <Placemark>\n");
		fprintf(_assist->optimusRoute, "    <name>%s</name>\n",aux1 -> name);
		fprintf(_assist->optimusRoute, "    <Point>\n");
		fprintf(_assist->optimusRoute, "      <coordinates>%#g,%#g,0</coordinates>\n",aux1 -> lat,aux1 -> lng);
		fprintf(_assist->optimusRoute, "    </Point>\n");
		fprintf(_assist->optimusRoute, "  </Placemark>\n");
		first = 0;
	}
	
	fprintf(_assist->optimusRoute, "  <Placemark>\n");
	fprintf(_assist->optimusRoute, "    <name>%s</name>\n",aux2 -> name);
	fprintf(_assist->optimusRoute, "    <Point>\n");
	fprintf(_assist->optimusRoute, "      <coordinates>%#g,%#g,0</coordinates>\n",aux2 -> lat,aux2 -> lng);
	fprintf(_assist->optimusRoute, "    </Point>\n");
	fprintf(_assist->optimusRoute, "  </Placemark>\n");

	
	fprintf(_assist->optimusRoute, "  <Placemark>\n");
	fprintf(_assist->optimusRoute, "    <LineString>\n");
	fprintf(_assist->optimusRoute, "      <extrude>1</extrude>\n");
	fprintf(_assist->optimusRoute, "      <tessellate>1</tessellate>\n");
	fprintf(_assist->optimusRoute, "      <name>%s-%d</name>\n",flight->name,flight->flightnumber);
	fprintf(_assist->optimusRoute, "      <coordinates>\n        %#g,%#g,0\n        %#g,%#g,0\n      </coordinates>\n",aux1 -> lat,aux1 -> lng,aux2 -> lat,aux2 -> lng);
	fprintf(_assist->optimusRoute, "    </LineString>\n  </Placemark>\n");
	
	airFree(aux1);
	airFree(aux2);
	
	return;
}
	

static void
calculateArrivalTime(flightADT flight, int acthours, int actminutes, int militime)
{
	int days, hours, minutes;
	airportADT aux1, aux2;
	airportCDT src, tgt;
	
	days =  0;
	/* Obtiene cantidad de horas y minutos */
	hours = militime / 60;
	minutes = militime % 60;

	if (minutes >= 60)
	{
		hours += minutes / 60;
		minutes -= (minutes - (minutes % 60));
	}
	if (hours >= 24)
	{
		days += hours / 24;
		hours -= (hours - (hours % 24));
	}
	/* hasta aqui se calcularon la cantidad de dias, horas y minutos que se tardo en llegar
	 * al proximo aeropuerto */

	
	actminutes += minutes;

	if (actminutes >= 60)
	{
		acthours += actminutes / 60;
		actminutes -= (actminutes - (actminutes % 60));
	}

	acthours += hours;

	if (acthours >= 24)
	{
		days += acthours / 24;
		acthours -= (acthours - (acthours % 24));
	}

	src.lat = src.lng = tgt.lat = tgt.lng = IGNR_LAT_LNG;
	strcpy(src.name, flight -> airfrom);
	strcpy(tgt.name, flight -> airto);
	
	aux1 = GetAirportFromGraph(&src);
	aux2 = GetAirportFromGraph(&tgt);

	aux2 -> arrival.arrivalTime = MiliHour(acthours, actminutes);
	SetCurrentDay(aux1, aux2, days);
	return;
}

static void
SetCurrentDay(airportADT src, airportADT tgt, int acumdays)
{
	char * days[DAYS] = {"Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"};
	int i;
	int actday;

	/* Busca el dia actual */
	for (i = 0 ; i < DAYS ; i++)
		if (strcmp(src -> arrival.currday, days[i]) == 0 )
			actday = i;

	/* Setea el dia de arrivo */
	strncpy(tgt -> arrival.currday, days[(actday + acumdays) % DAYS], DAY_MAX);

	return;
}


int PrintAirportsAsKML(void) 
{
	nodeElementT * airports;
	airportCDT * airport;
	int n, i;
	
	airports = getNodesInfo(_assist->graph, &n);
	
	for(i = 0; i < n; i++) {
		airport = airports[i];
		printf("\n %s -- %g:%g \n", airport->name, airport->lat, airport->lng);

	}
	return 0;
}
